package com.tencent.iov.cowin.befsale.clue.service.clue.impl;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.tencent.iov.cowin.befsale.clue.controller.clue.assembler.ClueAssembler;
import com.tencent.iov.cowin.befsale.clue.controller.clue.assembler.ClueDayStatsAssembler;
import com.tencent.iov.cowin.befsale.clue.controller.clue.vo.request.UpdateClueDayStatsRequest;
import com.tencent.iov.cowin.befsale.clue.controller.cus.vo.request.ClueIntentionGradeDataRequest;
import com.tencent.iov.cowin.befsale.clue.dao.http.mdquery.MdQueryClient;
import com.tencent.iov.cowin.befsale.clue.dao.http.mdquery.response.EmployeeResponse;
import com.tencent.iov.cowin.befsale.clue.dao.http.qwSystem.QwSystmeClient;
import com.tencent.iov.cowin.befsale.clue.dao.http.qwSystem.request.UserBehaviorDateRequest;
import com.tencent.iov.cowin.befsale.clue.dao.http.qwSystem.response.UserBehaviorResponse;
import com.tencent.iov.cowin.befsale.clue.domain.clue.po.ClueDayStats;
import com.tencent.iov.cowin.befsale.clue.repository.clue.ClueDayStatsManager;
import com.tencent.iov.cowin.befsale.clue.service.clue.ClueDayStatsService;
import com.tencent.iov.cowin.befsale.clue.service.clue.ClueService;
import com.tencent.iov.cowin.befsale.clue.service.cus.DealerCustomerService;
import com.tencent.iov.cowin.befsale.clue.service.cus.OemCustomerService;
import com.tencent.iov.cowin.befsale.clue.value.enums.ClueStatsEnum;
import com.tencent.iov.cowin.befsale.clue.value.enums.FollowRecordSecondStatusEnum;
import com.tencent.iov.cowin.befsale.clue.value.enums.FollowRecordStatusEnum;
import com.software.boot.common.entity.Response;
import com.software.boot.common.model.dao.PageRequest;
import com.software.boot.common.model.dao.PageResponse;
import com.software.boot.common.util.BeanUtil;
import com.software.boot.common.util.DateUtil;
import com.software.boot.common.util.EmptyUtils;
import com.software.boot.common.util.ValidateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Describe: 线索统计看板表_Service实现层
 * @author: zly
 * @created: 2022-01-14 09:42:52
 * @version: 1.0
 */
@Slf4j
@Service
public class ClueDayStatsServiceImpl implements ClueDayStatsService {

    @Autowired
    private ClueDayStatsManager clueDayStatsManager;
    @Autowired
    private ClueService clueService;
    @Autowired
    private MdQueryClient mdQueryClient;
    @Autowired
    private QwSystmeClient qwSystmeClient;
    @Autowired
    private OemCustomerService oemCustomerService;
    @Autowired
    private DealerCustomerService dealerCustomerService;

    //创建一个定长线程池，超出的线程会在队列中等待
    private ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4);

    /**
     * 通过ID获取详细信息
     *
     * @param: request
     * @return: ClueDayStats对象
     */
    @Override
    public ClueDayStats getDetailById(Long id) {
        /** 1 验证请求参数 */
        ValidateUtils.checkObjNull(id, "请求参数不能为空");
        ValidateUtils.checkIntNotNullAndGTZero(id.intValue(), "ID不能为空或小于0");
        /** 2 处理业务 */
        ClueDayStats clueDayStats = clueDayStatsManager.getDetailById(id);
        if (EmptyUtils.isNotEmpty(clueDayStats)) {
            return BeanUtil.copy(clueDayStats, ClueDayStats.class);
        }
        /** 3 返回结果 */
        return null;
    }

    /**
     * 单条数据插入ClueDayStats
     *
     * @param request
     * @return 插入的条数
     */
    @Override
    public int insert(ClueDayStats request) {
        /** 1 验证请求参数 */
        /** 2 处理业务 */
        int count = clueDayStatsManager.insert(request);
        /** 3 封装返回值 */
        return count;
    }

    /**
     * 批量插入ClueDayStats
     *
     * @param requests
     * @return 批量插入的条数
     */
    @Override
    public int insertBatch(List<ClueDayStats> requests) {
        /** 1 验证请求参数 */
        /** 2 处理业务 */
        int count = clueDayStatsManager.insertBatch(requests);
        /** 3 封装返回值 */
        return count;
    }


    /**
     * 单条ClueDayStats更新
     *
     * @param request
     * @return 更新的条数
     */
    @Override
    public int update(ClueDayStats request) {

        /** 1 验证请求参数 */
        /** 2 处理业务 */
        int count = clueDayStatsManager.update(request);
        /** 3 返回对象 */
        return count;
    }

    /**
     * 通过ID删除数据
     *
     * @param request
     * @return 删除的条数
     */
    @Override
    public int delete(ClueDayStats request) {
        Long id = request.getId();
        /** 1 验证请求参数 */
        ValidateUtils.checkObjNull(id, "请求参数不能为空");
        ValidateUtils.checkIntNotNullAndGTZero(id.intValue(), "ID不能为空或小于0");
        /** 2 处理业务 */
        ClueDayStats clueDayStats = new ClueDayStats();
        clueDayStats.setId(id);
//		clueDayStats.setDelete(DeleteEnum.DETELED.getCode());
        int count = clueDayStatsManager.update(clueDayStats);
        /** 3 返回对象 */
        return count;
    }

    /**
     * 根据条件查询对象对象list列表
     *
     * @param request
     * @return 查询结果
     */
    @Override
    public List<ClueDayStats> getListByParam(ClueDayStats request) {
        /** 1 验证请求参数 */
        //可以设置排序
        /** 2 处理业务 */
        List<ClueDayStats> queryList = clueDayStatsManager.getListByParam(request);
        /** 3 返回结果 */
        if (CollectionUtils.isNotEmpty(queryList)) {
            return BeanUtil.copyByList(queryList, ClueDayStats.class);
        }
        return new ArrayList<>();
    }

    /**
     * 根据条件查询对象list记录
     *
     * @param request
     * @return 分页对象
     */
    @Override
    public PageResponse<ClueDayStats> getPageByParam(PageRequest<ClueDayStats> request) {
        /** 1 验证请求参数 */
        ValidateUtils.checkObjNull(request, "请求参数不能为空");
        //可以设置排序 query.orderByCreatedTime(true);
        /** 2 处理业务 */
        RowBounds rowBounds = new RowBounds(request.getPageNum(), request.getPageSize());
        Page<ClueDayStats> queryPageList = clueDayStatsManager.getPageByParam(request.getParamData(), rowBounds);
        PageResponse<ClueDayStats> pageResponse = new PageResponse<>();
        pageResponse.converPageResponse(pageResponse, queryPageList, request);
        pageResponse.setDataList(BeanUtil.copyByList(queryPageList.getResult(), ClueDayStats.class));
        /** 3 返回结果 */
        return pageResponse;
    }

    /**
     * 根据条件查询单个对象
     *
     * @param request
     * @return
     */
    @Override
    public ClueDayStats getOneByParam(ClueDayStats request) {
        return clueDayStatsManager.getOneByParam(request);
    }

    /**
     * 管理看板-顾问排行
     * add by zly 2022-01-14
     * @param clueDataRequest
     * @return
     */
    @Override
    public PageResponse<ClueDayStats> getClueTaskDataByType(PageRequest<ClueIntentionGradeDataRequest> clueDataRequest) {
        RowBounds rowBounds = new RowBounds(clueDataRequest.getPageNum(), clueDataRequest.getPageSize());
        ClueIntentionGradeDataRequest clueDataRequestParam=clueDataRequest.getParamData();
        //赋值租户，企微，经销商
        ClueAssembler.initClueIntentionGradeDataRequest(clueDataRequestParam);
        //根据传的时间类型计算开始时间结束时间
        clueDataRequestParam=clueService.initStartTimeEndTime(clueDataRequestParam);
        log.info("管理看板-顾问排行方法调用参数request={}", JSON.toJSONString(clueDataRequestParam));
        Page<ClueDayStats> queryPageList = clueDayStatsManager.getClueTaskDataByType(clueDataRequestParam, rowBounds);
        log.info("管理看板-顾问排行方法调用返回 queryPageList={}", JSON.toJSONString(queryPageList));
        PageResponse<ClueDayStats> pageResponse = new PageResponse<>();
        pageResponse.converPageResponse(pageResponse, queryPageList, clueDataRequest);
        pageResponse.setDataList(BeanUtil.copyByList(queryPageList.getResult(), ClueDayStats.class));
        /** 3 返回结果 */
        return pageResponse;
    }

    /**
     * 定时器执行-同步数据入库-同步线索管理统计数据入库
     * add by zly 2022-01-14
     * @param
     * @return
     */
    @Override
    public Boolean syncInsertOrUpdateBatch(String sysnTime) {
        ClueIntentionGradeDataRequest clueDataRequest=new ClueIntentionGradeDataRequest();
        /*
         *第一步：计算统计开始时间，结束时间,若有指定日期的则同步指定日期当天的，若没有指定，默认一天统计一次，统计昨天的
         * sysnTime:YYYY-MM-dd
         */
        LocalDate sysnDay = LocalDate.now().plusDays(-1);
        if(!StringUtils.isEmpty(sysnTime)){
            sysnDay=LocalDate.parse(sysnTime);
        }
        Date startTime = Date.from(LocalDateTime.of(sysnDay, LocalTime.MIN).atZone(ZoneId.systemDefault()).toInstant());
        Date endTime =Date.from(LocalDateTime.of(sysnDay, LocalTime.MAX).atZone(ZoneId.systemDefault()).toInstant());
        log.info("顾问排行数据每天定时同步-线索管理部分数据-统计开始时间startTime={},endTime={}",
                DateUtil.toDateString(startTime, DateUtil.DEFAULT_DATETIME_PATTERN),DateUtil.toDateString(endTime, DateUtil.DEFAULT_DATETIME_PATTERN));

        //第二步：批量同步宽表的顾问到统计表，
        clueDataRequest.setStartTime(startTime);
        clueDataRequest.setEndTime(endTime);
        //先查看库里是否有需要统计的顾问数据
        ClueDayStats sysnDataRequest = new ClueDayStats();
        sysnDataRequest.setStartTime(startTime);
        Integer count = clueDayStatsManager.checkTheDayEmpData(sysnDataRequest);
        if (count == null || count == 0) {
            //说明没有，则执行同步，然后拿到同步的数量，如果定时器一天执行多次，则同步顾问时的结束时间还是算成那天的23：59：59
            log.info("顾问排行数据每天定时同步-线索管理部分数据-宽表顾问同步到统计表参数clueDataRequest={}", JSON.toJSONString(clueDataRequest));
            clueDayStatsManager.sycnInsertBatch(clueDataRequest);
            count = clueDayStatsManager.checkTheDayEmpData(sysnDataRequest);
        }

        //第三步：查询统计表中 顾问名称或者图像，或者租户，或者企微，为空的顾问，然后去主数据查询，批量修改统计表中对应的信息
        List<Long> employeeIdList = clueDayStatsManager.getEmployeeIdList();
        if (CollectionUtils.isNotEmpty(employeeIdList)) {
            Response<List<EmployeeResponse>> empListRes = mdQueryClient.getEmployeeByEmpIds(employeeIdList);
            if (empListRes != null && empListRes.getData() != null && empListRes.getData().size() > 0) {
                List<EmployeeResponse> orgEmpList = empListRes.getData();
                List<UpdateClueDayStatsRequest> updateClueDayRequestList = new ArrayList<>();
                orgEmpList.stream().forEach(o -> {
                    UpdateClueDayStatsRequest dsRquest = new UpdateClueDayStatsRequest();
                    dsRquest.setTenantId(o.getTenantId());
                    dsRquest.setCorpId(o.getCorpId());
                    dsRquest.setDealerId(o.getDealerId());
                    dsRquest.setEmployeeId(o.getId());
                    dsRquest.setEmployeeName(o.getName());
                    dsRquest.setEmployeeImg(o.getAvatar());
                    dsRquest.setEmployeeUserId(o.getUserid());
                    updateClueDayRequestList.add(dsRquest);
                });
                clueDayStatsManager.updateBatchClueDayByEmpId(updateClueDayRequestList);
            }
        }

        //第四步：删除没有租户，企微，和userId得顾问数据
        clueDayStatsManager.deleteDateWithNoTenantId();

        //第五步：分页异步获取要同步的数据，默认每次同步5条顾问的数据,然后去计算线索的各个数据
        Integer pageSize = 5;
        sysnDataRequest.setPageSize(pageSize);
        Integer pageTotal = (count % pageSize) > 0 ? (count / pageSize) + 1 : count / pageSize;
        for (int i = 0; i < pageTotal; i++) {
            Integer pageStart = i * pageSize;
            fixedThreadPool.execute(new Runnable() {
                public void run() {
                    sysnEmpData(sysnDataRequest,clueDataRequest,pageStart);
                }
            });
        }
        return true;
     }

     private void sysnEmpData(ClueDayStats sysnDataRequest,ClueIntentionGradeDataRequest clueDataTime,Integer pageStart){
         ClueDayStats sysnSearchRequest= ClueDayStatsAssembler.transformPOToResponseVo(sysnDataRequest,ClueDayStats.class);
         sysnSearchRequest.setPageIndex(pageStart);
         log.info("顾问排行数据每天定时同步-线索管理部分数据-分页查看库里需要同步的数据sysnSearchRequest={},pageStart={}",JSON.toJSONString(sysnSearchRequest),pageStart);
        List<ClueDayStats> empList=clueDayStatsManager.getListByStartTimeEndTime(sysnSearchRequest);
        if(CollectionUtils.isEmpty(empList)){
            return ;
        }
         log.info("顾问排行数据每天定时同步-线索管理部分数据-从数据库查询的EmpListDat={}",JSON.toJSONString(empList));
        ClueIntentionGradeDataRequest clueDataRequest=new ClueIntentionGradeDataRequest();
         clueDataRequest.setStartTime(clueDataTime.getStartTime());
         clueDataRequest.setEndTime(clueDataTime.getEndTime());
        empList.stream().forEach(e->{
            //统计 客户转线索数
            clueDataRequest.setTenantId(e.getTenantId());
            clueDataRequest.setCorpId(e.getCorpId());
            clueDataRequest.setDealerId(e.getDealerId());
            clueDataRequest.setFollowEmpId(e.getEmployeeId());
            Long addPhoneNum=0l;
            if(StringUtils.isEmpty(e.getDealerId())||"0".equals(e.getDealerId())) {
                addPhoneNum= oemCustomerService.getEmpBindCount(clueDataRequest);
            }else{
                addPhoneNum = dealerCustomerService.getEmpBindCount(clueDataRequest);
            }
            e.setAddPhoneNum(addPhoneNum);

            //统计 新增订单数
            Long orderNum=clueService.getEmpFollowOrderNum(clueDataRequest);
            e.setOrderNum(orderNum);
            //统计 新增线索数量 查询在指定范围内，线索所属某顾问的数量
            Long clueNum=clueService.getEmpClueNumByTime(clueDataRequest);
            e.setClueNum(clueNum);
            List<Integer> secondStatusList;

            //统计 新增跟进数
            clueDataRequest.setClueStatus(ClueStatsEnum.FOLLOW_UP.getCode());
            clueDataRequest.setStatus(FollowRecordStatusEnum.FOLLOW_UP.getCode());
            //secondStatusList=new ArrayList<>();
            //secondStatusList.add(FollowRecordSecondStatusEnum.FOLLOW_CHECKIN.getCode());
            clueDataRequest.setSecondStatusList(null);
            log.info("顾问排行数据每天定时同步-线索管理部分数据-组装参数去查统计信息，新增跟进数clueDataRequest={}",JSON.toJSONString(clueDataRequest));
            Long followNum=clueService.getEmpNumByClueStatus(clueDataRequest);
            e.setFollowNum(followNum);

            //统计 新增试驾数
            clueDataRequest.setClueStatus(null);
            clueDataRequest.setStatus(null);
            secondStatusList=new ArrayList<>();
            secondStatusList.add(FollowRecordSecondStatusEnum.FOLLOW_DRIVE_TEST.getCode());
            clueDataRequest.setSecondStatusList(secondStatusList);
            log.info("顾问排行数据每天定时同步-线索管理部分数据-组装参数去查统计信息，新增试驾数clueDataRequest={}",JSON.toJSONString(clueDataRequest));
            Long driveNum=clueService.getEmpNumByClueStatus(clueDataRequest);
            e.setDriveNum(driveNum);

            //统计 新增到店数=线索记录表有新增到店接待 或 试乘试驾记录，则加上上面计算的试驾数量
            clueDataRequest.setClueStatus(ClueStatsEnum.CHECKIN.getCode());
            clueDataRequest.setStatus(FollowRecordStatusEnum.FOLLOW_UP.getCode());
            secondStatusList=new ArrayList<>();
            secondStatusList.add(FollowRecordSecondStatusEnum.FOLLOW_CHECKIN.getCode());
            clueDataRequest.setSecondStatusList(secondStatusList);
            Long checkinNum=clueService.getEmpNumByClueStatus(clueDataRequest);
            e.setCheckinNum(checkinNum+driveNum);

            //统计 新增实销数
            clueDataRequest.setClueStatus(ClueStatsEnum.CONSUMED.getCode());
            clueDataRequest.setStatus(null);
            secondStatusList=new ArrayList<>();
            secondStatusList.add(FollowRecordSecondStatusEnum.FOLLOW_CONSUMED.getCode());
            clueDataRequest.setSecondStatusList(secondStatusList);
            Long deliveryNum=clueService.getEmpNumByClueStatus(clueDataRequest);
            e.setDeliveryNum(deliveryNum);
        });
        //批量修改统计表的数据
        clueDayStatsManager.updateBatchClueDayById(empList);
    }

    /**
     * 定时器执行-同步数据入库-同步对接企微统计数据入库
     * add by zly 2022-01-16
     * @return
     */
    @Override
    public Boolean syncUpdateBatchWithQwData(String sysnTime) {
        /*
         *第一步：计算统计开始时间，结束时间
         *计算统计开始结束时间,因为统计企微的接口https://developer.work.weixin.qq.com/document/path/92132最新数据只更新到昨天过完，
         *所以统计开始时间也为昨天的 00:00:00 结束时间为昨天的 23:59:59
         */

        LocalDate sysnDay = LocalDate.now().plusDays(-1);
        if(!StringUtils.isEmpty(sysnTime)){
            sysnDay=LocalDate.parse(sysnTime);
        }
        Date startTime=Date.from(LocalDateTime.of(sysnDay, LocalTime.MIN).atZone(ZoneId.systemDefault()).toInstant());
        Date endTime= Date.from(LocalDateTime.of(sysnDay, LocalTime.MAX).atZone(ZoneId.systemDefault()).toInstant());
        Long startTimeLong=startTime.getTime()/1000;
        Long endTimeLong=endTime.getTime()/1000;
        log.info("顾问排行数据每天定时同步-统计企微部分-统计开始时间startTime={},endTime={},startTimeLong={},endTimeLong={}",
                DateUtil.toDateString(startTime, DateUtil.DEFAULT_DATETIME_PATTERN),DateUtil.toDateString(endTime, DateUtil.DEFAULT_DATETIME_PATTERN),
                startTimeLong,endTimeLong);

        /*第二步：分页异步获取要同步的数据，默认每次同步5条顾问的数据
         * 获取要同步的顾问的userId ，然后对接qwSystem获取对应的，新增申请数，新增客户数，聊天总数等
         * https://developer.work.weixin.qq.com/document/path/92132
         * 这个接口，他有说明，如传入多个userid，则表示获取这些成员总体的联系客户数据，而我们设计的是按顾问维度去统计的，
         * 所以调用接口，每次就只能传一个顾问的userId,所以库里有多少个顾问，这个接口每天就的被调用多少次。
         */
        ClueDayStats sysnDataRequest = new ClueDayStats();
        sysnDataRequest.setStartTime(startTime);
        Integer count = clueDayStatsManager.checkTheDayEmpData(sysnDataRequest);
        Integer pageSize = 5;
        sysnDataRequest.setPageSize(pageSize);
        Integer pageTotal = (count % pageSize) > 0 ? (count / pageSize) + 1 : count / pageSize;
        for (int i = 0; i < pageTotal; i++) {
            Integer pageStart = i * pageSize;
            fixedThreadPool.execute(new Runnable() {
                public void run() {
                    syncQwData(sysnDataRequest,startTimeLong,endTimeLong,pageStart);
                }
            });
        }
        return true;
    }

    private void syncQwData(ClueDayStats sysnDataRequest,Long startTimeLong,Long endTimeLong,Integer pageStart){
        ClueDayStats sysnSearchRequest= ClueDayStatsAssembler.transformPOToResponseVo(sysnDataRequest,ClueDayStats.class);
        sysnSearchRequest.setPageIndex(pageStart);
        log.info("顾问排行数据每天定时同步-统计企微部分-分页查看库里需要同步的数据sysnSearchRequest={},startTimeLong={},endTimeLong={},pageStart={}",JSON.toJSONString(sysnSearchRequest),startTimeLong,endTimeLong,pageStart);
        List<ClueDayStats> empList=clueDayStatsManager.getListByStartTimeEndTime(sysnSearchRequest);
        if(CollectionUtils.isEmpty(empList)){
            return;
        }
        empList.stream().forEach(e->{
            UserBehaviorDateRequest userBehaviorRequest=new UserBehaviorDateRequest();
            userBehaviorRequest.setUserId(e.getEmployeeUserId());
            userBehaviorRequest.setStartTime(startTimeLong);
            userBehaviorRequest.setEndTime(endTimeLong);
            Response<UserBehaviorResponse> behaviorRes=qwSystmeClient.getUserBehaviorData(userBehaviorRequest,e.getTenantId(), e.getCorpId());
            log.info("顾问排行数据每天定时同步-统计企微部分-调用企微，请求参数userBehaviorRequest={},tenantId={},corpId={},返回信息behaviorRes={}",JSON.toJSONString(userBehaviorRequest),e.getTenantId(),e.getCorpId(),JSON.toJSONString(behaviorRes));
            if(behaviorRes!=null&&behaviorRes.getData()!=null&&behaviorRes.getData().getBehaviorData()!=null&&behaviorRes.getData().getBehaviorData().size()>0){
                List<UserBehaviorResponse.BehaviorData> behaviorData=behaviorRes.getData().getBehaviorData();
                UserBehaviorResponse.BehaviorData bd=behaviorData.get(0);
                if(String.valueOf(startTimeLong).equals(String.valueOf(bd.getStatTime()))){
                    e.setApplyNum(Long.valueOf(String.valueOf(bd.getNewApplyCnt()==null?"0":bd.getNewApplyCnt())));//新增申请数
                    e.setAddCustomerNum(Long.valueOf(String.valueOf(bd.getNewContactCnt()==null?"0":bd.getNewContactCnt())));//新增客户数
                    e.setChatNum(Long.valueOf(String.valueOf(bd.getChatCnt()==null?"0":bd.getChatCnt())));//聊天总数
                    e.setMessageNum(Long.valueOf(String.valueOf(bd.getMessageCnt()==null?"0":bd.getMessageCnt())));//发送消息数
                    e.setBlockNum(Long.valueOf(String.valueOf(bd.getAvgReplyTime()==null?"0":bd.getAvgReplyTime())));//拉黑数
                }
            }
        });
        //批量修改统计表的数据 ,要为数据赋值初始值，要不然无法正常运行
        clueDayStatsManager.updateBatchClueDayById(empList);
    }

}
